<?php
# Useful constants
define( "BUGS_PER_PAGE", 25 );
define( "LOGS_PER_PAGE", 10 );
define( "MAX_LINKS", 5 );
define( "DESCRIPTION_LENGTH", 60 );
define( "STATUS_GUEST", 0 );
define( "STATUS_USER", 1 );
define( "STATUS_ADMIN", 2 );
define( "DOMAIN", "http://localhost/breast/" );

# Database functions
mysql_connect( "localhost", "root", "password" ) or outputAlert( "Database error", "The database server is not available." );
mysql_select_db( "BREaST" ) or outputAlert( "Database error", "The database is not available." );

function db_query( $query )
{
    return mysql_query( $query );
}

function db_fetch_object( $token )
{
    return mysql_fetch_object( $token );
}

function db_num_rows( $token )
{
    return mysql_num_rows( $token );
}

# Output a select box of a field
function outputField( $field, $name, $selected = null )
{
    print "<select name = '{$name}'>\n";
    if ( $field == "User" )
    {
        $result = db_query( "SELECT * FROM User WHERE User.status > 0 ORDER BY User.name" );
        if ( $selected == "None" )
            print "<option value = '0' selected = 'selected'>None</option>\n";
        else
            print "<option value = '0'>None</option>\n";
    }
    if ( $field == "Project" )
        $result = db_query( "SELECT * FROM Project WHERE Project.id IN ( SELECT UserProject.project FROM UserProject WHERE UserProject.user = {$_SESSION["user"]} ) ORDER BY Project.name" );
    if ( $field == "Priority" )
        $result = db_query( "SELECT * FROM Priority ORDER BY Priority.id" );
    if ( $field == "Status" )
        $result = db_query( "SELECT * FROM Status ORDER BY Status.id" );
    while ( $row = db_fetch_object( $result ) )
    {
        if ( $row->name == $selected )
        {
            print "<option value = '{$row->id}' selected = 'selected'>{$row->name}</option>\n";
        }
        else
        {
            print "<option value = '{$row->id}'>{$row->name}</option>\n";
        }
    }
    print "</select>\n";
}

# Output the page header
function outputHeader()
{
    # Get the current user's info
    $user = "You are nobody.";
    $feed = "";
    session_start();
    if ( isset( $_SESSION["user"] ) and is_numeric( $_SESSION["user"] ) )
    {
        # Get the user's name
        $result_name = db_query( "SELECT User.name FROM User WHERE User.id = {$_SESSION["user"]}" );
        $row_name = db_fetch_object( $result_name );

        # Get total bugs assigned
        $result_assigned = db_query( "SELECT User.name, User.feed, COUNT( Bug.id ) AS assigned FROM User, Bug, Status WHERE User.id = {$_SESSION["user"]} AND Bug.assigned = {$_SESSION["user"]} AND Bug.status = Status.id AND Status.name != 'Closed'" );
        $row_assigned = db_fetch_object( $result_assigned );

        # Get total bugs pending assignment
        $result_pending = db_query( "SELECT COUNT( Bug.id ) AS pending FROM Bug, Status, UserProject WHERE Bug.assigned = 0 AND Bug.status = Status.id AND Status.name != 'Closed' AND UserProject.user = {$_SESSION["user"]} AND UserProject.project = Bug.project" );
        $row_pending = db_fetch_object( $result_pending );

        # The user is a guest
        if ( hasStatus( STATUS_GUEST ) )
            $user = "You are {$row_name->name} | Editing disabled | <a href = 'logout.php'>Log out</a>";

        # The default user message
        if ( hasStatus( STATUS_USER ) )
            $user = "You are {$row_name->name} | {$row_assigned->assigned} assigned | {$row_pending->pending} pending assignment | <a href = 'logout.php'>Log out</a>";

        # The user is an admin
        if ( hasStatus( STATUS_ADMIN ) )
            $user = "You are {$row_name->name} | {$row_assigned->assigned} assigned | {$row_pending->pending} pending assignment | <a href = 'admin.php'>Admin</a> | <a href = 'logout.php'>Log out</a>";

        # Get the user's feed URL
        $feed = "<link rel = 'alternate' type = 'application/rss+xml' href = 'rss.php?" . $row_assigned->feed . "' title = 'BREaST' />";
    }

    print <<<EOF
<!DOCTYPE html>
<html lang = "en">
<head>
<meta http-equiv = "content-type" content = "text/html; charset=utf-8" />
<title>BREaST</title>
<link href="style.css" rel="stylesheet" type="text/css" media="screen" />
{$feed}
</head>
<body>
<h1><a href = "main.php">Bug Reporting, Eliminating, and Sorting Tool</a></h1>
<h2>{$user}</h2>

EOF;
}

# Output the page footer
function outputFooter()
{
    print "</body></html>\n";
}

# Output an alert page
function outputAlert( $title, $message )
{
    outputHeader();
    print "<div class = 'error'>{$title}</div>\n";
    print "<p>{$message}</p>\n";
    outputFooter();
    exit();
}

# Get the total number of bugs in the current view
function getTotalBugs()
{
    if ( isset( $_GET["show"] ) )
        $result = db_query( "SELECT Bug.id FROM Bug, Project, UserProject WHERE Project.id = Bug.project AND UserProject.project = Bug.project AND UserProject.user = {$_SESSION["user"]}" );
    else
        $result = db_query( "SELECT Bug.id FROM Bug, Status, Project, UserProject WHERE Bug.status = Status.id AND Status.name != 'Closed' AND Project.id = Bug.project AND UserProject.project = Bug.project AND UserProject.user = {$_SESSION["user"]}" );
return db_num_rows( $result );
}

# Get the total number of logs for the current bug
function getTotalLogs( $bug )
{
    if ( ! isValidEntry( "Bug", $bug ) )
        return false;
    return db_num_rows( db_query( "SELECT Log.id FROM Log WHERE Log.bug = {$bug} AND Log.initial = 0" ) );
}

# See if a given page number is valid
function isValidPage( $page )
{
    if ( is_numeric( $page ) and $page > 0 and $page <= ceil( getTotalBugs() / BUGS_PER_PAGE ) )
        return true;
    else
        return false;
}

# See if a given log page number is valid
function isValidLogPage( $bug, $page )
{
    if ( is_numeric( $page ) and $page > 0 and $page <= ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE ) )
        return true;
    else
        return false;
}

# Output formatted pagination links
function outputPageLinks()
{
    # Assume page one if the page variable is bad
    if ( ! isValidPage( $_GET["page"] ) )
        $_GET["page"] = 1;

    # Assume initial page limits
    $bottom_page = $_GET["page"] - MAX_LINKS;
    $top_page = $_GET["page"] + MAX_LINKS;

    # We are really close to the bottom
    if ( max( $_GET["page"] - MAX_LINKS, 1 ) == 1 )
    {
        $bottom_page = 1;

        # Make up for the difference on the upper side
        $top_page = min( 2 * MAX_LINKS + 1, ceil( getTotalBugs() / BUGS_PER_PAGE ) );
    }

    # We are really close to the top
    if ( min( $_GET["page"] + MAX_LINKS, ceil( getTotalBugs() / BUGS_PER_PAGE ) ) == ceil( getTotalBugs() / BUGS_PER_PAGE ) )
    {
        $top_page = ceil( getTotalBugs() / BUGS_PER_PAGE );

        # Make up for the difference on the lower side
        $bottom_page = max( ceil( getTotalBugs() / BUGS_PER_PAGE ) - 2 * MAX_LINKS, 1 );
    }

    print "<p>";
    if ( getTotalBugs() > BUGS_PER_PAGE )
    {
        for ( $i = $bottom_page; $i <= $top_page; $i++ )
        {
            if ( $i == $_GET["page"] or $i == 1 and $_GET["page"] == "" )
                print "<strong>$i</strong> ";
            else
                print "<a href = 'main.php?" . getFullLink( $i, "", "" ) . "'>{$i}</a> ";
        }
    }
    if ( isset( $_GET["show"] ) )
        print "<a href = 'main.php?" . getFullLink( "1", "", "hide" ) . "'>hide closed</a></p>\n";
    else
        print "<a href = 'main.php?" . getFullLink ( "1", "", "show" ) . "'>show closed</a></p>\n";
}

# Output formatted pagination links
function outputLogLinks( $bug )
{
    # Assume page one if the page variable is bad
    if ( ! isValidLogPage( $bug, $_GET["page"] ) )
        $_GET["page"] = 1;

    # Assume initial page limits
    $bottom_page = $_GET["page"] - MAX_LINKS;
    $top_page = $_GET["page"] + MAX_LINKS;

    # We are really close to the bottom
    if ( max( $_GET["page"] - MAX_LINKS, 1 ) == 1 )
    {
        $bottom_page = 1;

        # Make up for the difference on the upper side
        $top_page = min( 2 * MAX_LINKS + 1, ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE ) );
    }

    # We are really close to the top
    if ( min( $_GET["page"] + MAX_LINKS, ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE ) ) == ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE ) )
    {
        $top_page = ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE );

        # Make up for the difference on the lower side
        $bottom_page = max( ceil( getTotalLogs( $bug ) / LOGS_PER_PAGE ) - 2 * MAX_LINKS, 1 );
    }

    if ( getTotalLogs( $bug ) > LOGS_PER_PAGE )
    {
        print "<p>page: ";
        for ( $i = $bottom_page; $i <= $top_page; $i++ )
        {
            if ( $i == $_GET["page"] or $i == 1 and $_GET["page"] == "" )
            {
                print "<strong>$i</strong> ";
            }
            else
            {
                print "<a href = 'viewBug.php?id={$bug}&amp;page={$i}'>{$i}</a> ";
            }
        }
        print "</p>\n";
    }
}

# Verify the user is logged in with proper privileges and toss them if necessary
function checkLogin( $status, $id = null )
{
    session_start();

    # They must be logged in
    if ( ! isLoggedIn() )
    {
        if ( is_numeric( $id ) )
            logout( "index.php?id={$id}" );
        else
            logout();
    }

    # They need the proper status
    if ( ! hasStatus( $status ) )
        outputAlert( "Insufficient privileges", "You do not have sufficient privileges to perform this action." );
}

# See if the user has proper status
function hasStatus( $status, $user = null )
{
    if ( ! isset( $user ) )
        $user = $_SESSION["user"];
    $result = db_query( "SELECT User.id FROM User WHERE User.id = {$user} AND User.status >= {$status}" );
    if ( db_num_rows( $result ) == 1 )
        return true;
    else
        return false;
}

# See if the user is logged in
function isLoggedIn()
{
    session_start();

    if ( isset( $_SESSION["user"] ) and is_numeric( $_SESSION["user"] ) )
    {
        # They must not be locked
        $result = db_query( "SELECT * FROM User WHERE User.id = {$_SESSION["user"]} AND User.locked = false" );
        if ( db_num_rows( $result ) == 1 )
            return true;
    }
    else
    {
        return false;
    }
}

# Log the user out and redirect them
function logout( $redirect = "index.php" )
{
    session_start();
    unset( $_SESSION["user"] );
    session_destroy();
    header( "Location: {$redirect}" );
}

# Get a link that retains current information
function getFullLink( $page, $sort, $show )
{
    # Set values
    if ( $page == "" )
        $page = $_GET["page"] == "" ? 1 : $_GET["page"];
    if ( $sort == "" )
        $sort = $_GET["sort"];
    if ( $show == "" and isset( $_GET["show"] ) )
        $show = "show";
    if ( $show == "hide" )
        $show = "";
    
    # Construct link
    $link = "page={$page}";
    if ( $sort != "" )
        $link .= "&amp;sort={$sort}";
    if ( $show != "" )
        $link .= "&amp;show";
    return $link;
}

# Get a table entry name by its ID
function getNameByID( $table, $id )
{
    # Needs to be a number
    if ( ! is_numeric( $id ) )
        return null;

    # Special case for no user
    if ( $table == "User" and $id == 0 )
        return "None";

    # Query for a name
    if ( $table == "Priority" )
        $query = "SELECT Priority.name FROM Priority WHERE id = {$id}";
    else if ( $table == "Project" )
        $query = "SELECT Project.name FROM Project WHERE id = {$id}";
    else if ( $table == "Status" )
        $query = "SELECT Status.name FROM Status WHERE id = {$id}";
    else if ( $table == "User" )
        $query = "SELECT User.name FROM User WHERE id = {$id}";
    else
        return null;

    $result = db_query( $query );
    if ( db_num_rows( $result ) == 1 )
    {
        $row = db_fetch_object( $result );
        return $row->name;
    }
    else
    {
        return null;
    }
}

# Check for a valid entry
function isValidEntry( $table, $id )
{
    # All entries are numbers
    if ( ! is_numeric( $id ) )
        return false;

    # Special case for no user
    if ( $table == "User" and $id == 0 )
        return true;

    # Query for a valid entry
    if ( $table == "Priority" )
        $query = "SELECT Priority.id FROM Priority WHERE id = {$id}";
    else if ( $table == "Project" )
        $query = "SELECT Project.id FROM Project WHERE id = {$id}";
    else if ( $table == "Status" )
        $query = "SELECT Status.id FROM Status WHERE id = {$id}";
    else if ( $table == "User" )
        $query = "SELECT User.id FROM User WHERE id = {$id}";
    else if ( $table == "Bug" )
        $query = "SELECT Bug.id FROM Bug WHERE id = {$id}";
    else if ( $table == "Log" )
        $query = "SELECT Log.id FROM Log WHERE id = {$id}";
    else
        return false;

    $result = db_query( $query );
    if ( db_num_rows( $result ) == 1 )
        return true;
    else
        return false;
}

# Add a log entry
function addLog( $bug, $comment, $changes, $user, $initial = 0 )
{
    if ( ! isValidEntry( "Bug", $bug ) )
        return false;
    if ( ! isValidEntry( "User", $user ) )
        return false;
    if ( ! isValidEntry( "Project", $changes["project"] ) )
        $changes["project"] = -1;
    if ( ! isValidEntry( "User", $changes["assigned"] ) )
        $changes["assigned"] = -1;
    if ( ! isvalidEntry( "Priority", $changes["priority"] ) )
        $changes["priority"] = -1;
    if ( ! isValidEntry( "Status", $changes["status"] ) )
        $changes["status"] = -1;
    if ( $initial != 0 and $initial != 1 )
        $initial = 0;

    $comment = addslashes( $comment );
    return db_query( "INSERT INTO Log VALUES( NULL, {$bug}, {$user}, '{$comment}', NOW(), {$changes["project"]}, {$changes["assigned"]}, {$changes["priority"]}, {$changes["status"]}, {$initial} )" );
}

# Is the user allowed to view a given bug?
function canSeeBug( $bug )
{
    # Confirm the bug is valid
    if ( ! isValidEntry( "Bug", $bug ) )
        return false;

    # Check the database
    if ( db_num_rows( db_query( "SELECT * FROM UserProject WHERE UserProject.user = {$_SESSION["user"]} AND UserProject.project IN ( SELECT Bug.project FROM Bug WHERE Bug.id = {$bug} )" ) ) > 0 )
        return true;
    else
        return false;
}

# Is the user allowed to view a given project?
function canSeeProject( $project )
{
    # Confirm the project is valid
    if ( ! isValidEntry( "Project", $project ) )
        return false;

    # Check the database
    if ( db_num_rows( db_query( "SELECT * FROM UserProject WHERE UserProject.user = {$_SESSION["user"]} AND UserProject.project = {$project}" ) ) > 0 )
        return true;
    else
        return false;
}

# Format a timestamp in a readable fashion
function formatTimestamp( $timestamp )
{
    return date( "j M Y h:i A", strtotime( $timestamp ) );
}
?>
